iT邦幫忙

2024 iThome 鐵人賽

DAY 30
0
生成式 AI

Semantic Kernel 的魔力-用.NET探索生成式應用系列 第 30

RAG 篇章 - 使用 Kernel Memory 與 Qdrant 向量資料庫實作 RAG

  • 分享至 

  • xImage
  •  

在前面二篇的文章裡,範例均是以文字陣列資料做為向量處理的來源資料,而實務上可能面臨更多的來源資料格式,包含:Web pages、PDF、Word、Markdown....等,這時候使用 Semantic Kernel 就顯得有點力不從心了,因為 Semantic Kernel 只能支援使用文字來源,所以當面對非單純文字來源的格式,必須依賴開發者自行處理資料的讀取轉成文字再由 Semantic Kernel 的 memory 模組做向量處理。

事實上從 Semantic Kernel 早期發展時就有延伸另一個套件 Kernel Memory,它是基於 Semantic Kernel 和 Semantic Memory 中收到的回饋和經驗獨立發展的服務,支援多種資料來源格式,提供更多的儲存服務,因此本篇最後一天的鐵人賽文章,就用 Kernel Memory 與 Qdrant 向量資料庫實作 RAG 來做個結尾。

實作 RAG

本次範例從全國法規庫取得 2 份PDF法規文件,分別是"所得稅法"以及"勞基法",示範使用 Kernel Memory 轉入 Qdrant 向量資料庫儲存,並且以問答方式從文件中取得回覆,而這個回覆是經過生成後的回覆,實現自然語言的回應,並非原始內容直接輸出。

  • 安裝 Microsoft.KernelMemory.Core 套件
dotnet add package Microsoft.KernelMemory.Core
  • 準備 embedding 模型以及 TextGeneration 模型
    這裡我使用部署在 Azure OpenAI 上的 text-embedding-3-large 以及 gpt-4o-mini 模型。Kernel Memory 支援多種雲服務及地端環境的模型連結,因此可以根據需求進行替換,也可以自行實作連結器。
var aoaiEmbeddingConfig = new AzureOpenAIConfig()
{
    Auth = AzureOpenAIConfig.AuthTypes.APIKey,
    APIKey = Config.aoai_apiKey,
    Endpoint = Config.aoai_endpoint,
    APIType = AzureOpenAIConfig.APITypes.ChatCompletion,
    Deployment = Config.aoai_embedding_deployment
};

var aoaiTextGenConfig = new AzureOpenAIConfig()
{
    Auth = AzureOpenAIConfig.AuthTypes.APIKey,
    APIKey = Config.aoai_apiKey,
    Endpoint = Config.aoai_endpoint,
    APIType = AzureOpenAIConfig.APITypes.ChatCompletion,
    Deployment = Config.aoai_deployment
};
  • 建立 MemoryServerless 物件
    設定模型以及 Qdrant 向量資料庫的連結。
 var memory = new KernelMemoryBuilder()
 .WithAzureOpenAITextEmbeddingGeneration(aoaiEmbeddingConfig)
 .WithAzureOpenAITextGeneration(aoaiTextGenConfig)
 .WithQdrantMemoryDb("http://localhost:6333")
 .Build<MemoryServerless>();
  • 轉入向量資料庫
    指定相同的 index 值,表示二份文件資料轉成向量後儲存在Qdrant 向量資料庫時,是放在同一個 Collection 裡(在Qdrant 向量資料庫裡,1個 Collection 可以簡單視為一個DB的概念)。
    tags 做為額外的metadata資訊,它的作用可以做為搜尋時的 filter 條件。
await memory.ImportDocumentAsync("所得稅法.pdf", index: "Demo_KM", documentId: "1", tags: new() { { "Law", "Tax" } });
await memory.ImportDocumentAsync("勞動基準法.pdf", index: "Demo_KM", documentId: "2", tags: new() { { "Law", "Labor" } });
  • 提問1
    呼叫 AskAsync 方法,指定 index 值。輸出的結果內容,根據向量相似搜尋參考資訊,透過 TextGeneration 模型,產生生成式回覆,並附帶來源資訊。
var ansewer = new MemoryAnswer();
ansewer = await memory.AskAsync("加班可以不給加班費嗎", index: "Demo_KM");
Console.WriteLine(ansewer);

//輸出結果
// 根據勞動基準法的規定,雇主在延長勞工工作時間時,必須依照法定標準支付加班費。具體來說,延長工作時間的工資應按照以下標準加給:

// 1. 延長工作時間在兩小時以內者,按平日每小時工資額加給三分之一以上。
// 2. 再延長工作時間在兩小時以內者,按平日每小時工資額加給三分之二以上。
// 3. 若依相關規定延長工作時間,則按平日每小時工資額加倍發給。

// 因此,雇主不得隨意不支付加班費,否則將違反勞動基準法的相關規定。若雇主未按期給付工資,主管機關可限期令其給付,勞工也有權要求支付應得的加班費。
// - Sources:
//   - 勞動基準法.pdf [2024年10月13日]
  • 提問2
    呼叫 AskAsync 方法,指定 index 值,並且外加 filter 條件。輸出的結果內容,類以於上一個提問,但可以發現文字並非100%一樣,這也證實其回覆是整理後的回應,而非原始文件內的文字內容。
ansewer = await memory.AskAsync("加班可以不給加班費嗎", index: "Demo_KM", filter: new MemoryFilter().ByTag("Law", "Labor"));
Console.WriteLine(ansewer);

//輸出結果
// 根據勞動基準法的規定,雇主在延長勞工工作時間時,必須依照法定標準支付加班費。具體來說,雇主延長勞工工作時間的工資應按照以下標準加給:

// 1. 延長工作時間在兩小時以內者,按平日每小時工資額加給三分之一以上。
// 2. 再延長工作時間在兩小時以內者,按平日每小時工資額加給三分之二以上。
// 3. 若依相關規定延長工作時間,則按平日每小時工資額加倍發給。

// 因此,雇主不得隨意不支付加班費,否則將違反勞動基準法的相關規定。若雇主未按期給付工資,主管機關可限期令其給付,並且勞工有權要求支付應得的加班費。
// - Sources:
//   - 勞動基準法.pdf [2024年10月13日]
  • 提問3
    設定相關性閥值,避免搜尋太多相關性太低的資料,導致回覆的正確性。
    例如,當沒有設定閥值時(預設值 : 0),相同的問題,得到的回覆不正確,原因是在所得稅文件中,有提及加班費,但並不是在討論要不要給加班費,而是關於計稅問題,但卻跟使用者的提問混在一起,導致回覆的正確性並不好。
    當進一步設定相關性閥值,由於指定filter所得稅,所以找不到參考資料,以致無法回覆(INFO NOT FOUND),在本例中這才是期望得到的回應。
Console.WriteLine("\n\n");
ansewer = await memory.AskAsync("加班可以不給加班費嗎", index: "Demo_KM", filter: new MemoryFilter().ByTag("Law", "Tax"));
Console.WriteLine(ansewer);

//輸出結果
// 根據一般的勞動法規,加班通常應支付加班費。然而,具體情況可能因國家或地區的法律而異。在某些情況下,例如員工的薪資結構中已包含加班費,或是根據勞動合同的約定,可能會出現不支付加班費的情況。

// 在台灣,根據《勞動基準法》,雇主應支付加班費,除非有特別的約定或情況。因此,若雇主未依法律規定支付加班費,則可能違反相關法律。

// 總之,通常情況下,加班應支付加班費,除非有明確的法律或合同規定可以不支付。
// - Sources:
//   - 所得稅法.pdf [2024年10月13日]


Console.WriteLine("\n\n");
ansewer = await memory.AskAsync("加班可以不給加班費嗎", index: "Demo_KM", filter: new MemoryFilter().ByTag("Law", "Tax"),minRelevance:0.8);
Console.WriteLine(ansewer);

//輸出結果
// INFO NOT FOUND

結語

終於來到30天完賽囉,這30天重新介紹了我一直在使用的 Semantic Kernel,時間倉促內容安排不是那麼的流暢,而其實還有很多想寫的內容,接下來如果大家還有興趣觀注 Semantic Kernel 的話,可以訂閱我的blog以及我在facebook上成立的SK社團。

我的blog:點這
facebook的SK社團:點這

想了解更仔細的RAG以及Qdrant 向量資料庫,容我工商一下囉
我合著的新書:點這


上一篇
RAG 篇章 - 使用 Qdrant 向量資料庫
系列文
Semantic Kernel 的魔力-用.NET探索生成式應用30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言